#ifndef CHAIN_OF_RESPONSIBILITY_HPP__
#define CHAIN_OF_RESPONSIBILITY_HPP__

// 责任链设计模式
#include <string>
#include <ctime>
#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <memory>
#include <filesystem>
#include <fstream>
#include <sys/wait.h>

using std::cin;
using std::cout;
using std::endl;
using std::string;

const string default_filepath = "./tmp";
const string default_filename = "message.txt";
const int default_max_line = 5;

// 创建一个基类
class HandlerText
{
public:
    virtual void Excute(const string &message) = 0;

    // 提供一个接口进行设置next指针
    void SetNextHandler(std::shared_ptr<HandlerText> handler)
    {
        next_handler = handler;
    }

protected:
    std::shared_ptr<HandlerText> next_handler;
    bool isopen_ = true;
};

// 完成第一个任务 , 对信息进行封装
class HandlerTextPackage : public HandlerText
{
    string GetCurrentTime()
    {
        time_t now = time(nullptr);
        struct tm *local_time = std::localtime(&now);
        char time_str[100]{};
        snprintf(time_str, sizeof(time_str), "[%04d-%02d-%02d %02d:%02d:%02d]",
                 local_time->tm_year + 1900, local_time->tm_mon + 1, local_time->tm_mday,
                 local_time->tm_hour, local_time->tm_min, local_time->tm_sec);

        return time_str;
    }

public:
    void Excute(const string &message)
    {
        string package_message = message;
        if (isopen_)
        {
            // 1. 在消息中添加时间 , 以及进程的PID信息
            string time_info = GetCurrentTime();
            // 将进程PID和时间拼接到 数据前面
            package_message = std::to_string(getpid()) + " " + time_info + " : " + message;
        }
        cout << "打包完成的消息: " << package_message << '\n';
        if (next_handler != nullptr)
            next_handler->Excute(package_message);
        else
            std::cout << "end of chain" << '\n';
    }
};

// 第二步,将数据保存到文件中
class HandlerTextSaveFile : public HandlerText
{
    void File_Exist(const string &filepath)
    {
        if (!std::filesystem::exists(filepath))
        {
            // 创建目录可能失败,因为没有权限,try catch捕获异常
            try
            {
                std::filesystem::create_directories(filepath);
            }
            catch (const std::filesystem::filesystem_error &e)
            {
                cout << e.what() << "int file :" << __FILE__ << " on line : " << __LINE__ << endl;
            }
        }
    }

public:
    HandlerTextSaveFile(const string &filepath, const string &filename) : filepath_(filepath), filename_(filename)
    {
    }

    void Excute(const string &message)
    {
        if (isopen_)
        {
            // 将数据保存到文件中
            // 1. 是否存在目录
            // 2. 是否存在文件
            // 3. 写入数据
            // 4. 关闭文件

            File_Exist(filepath_); // 判断路径是否存在
            string file = filepath_ + "/" + filename_;
            std::ofstream ofs(file, std::ios::app);
            ofs << message << std::endl;
            ofs.close();
        }
        cout << "文件保存完成" << '\n';
        if (next_handler != nullptr)
            next_handler->Excute(message);
        else
            std::cout << "end of chain" << '\n';
    }

private:
    string filepath_;
    string filename_;
};

// 第三步,是否对文件进行压缩,如果压缩要删除源文件
class HandlerTextTarGz : public HandlerText
{
    // 检查是否需要进行压缩
    bool IfNeedTar(const string &file)
    {
        int line_count = 0;
        string line;
        std::ifstream ofs(file);
        while (std::getline(ofs, line))
            line_count++;
        cout << "line_count: " << line_count << '\n';
        return line_count > max_line_;
    }

    // 改名
    string NewFileName(const string &filename)
    {
        time_t now = time(nullptr);
        struct tm *local_time = std::localtime(&now);
        char time_str[100]{};
        snprintf(time_str, sizeof(time_str), "%04d%02d%02d_%02d%02d%02d",
                 local_time->tm_year + 1900, local_time->tm_mon + 1, local_time->tm_mday,
                 local_time->tm_hour, local_time->tm_min, local_time->tm_sec);

        string ret = filename + "_" + time_str + ".tar.gz";
        return ret;
    }

public:
    HandlerTextTarGz(const string &filepath, const string &filename, const int max_line) : filepath_(filepath), filename_(filename), max_line_(max_line)
    {
    }

    void Excute(const string &message)
    {
        if (isopen_)
        {
            // 1.先判断是否需要进行压缩
            string file = filepath_ + "/" + filename_; // .tmp/message.txt

            if (IfNeedTar(file))
            {
                // 1.先要进入到目录中
                // 2.使用进程替换来进行压缩,要先将文件改名,再进行压缩
                // 3.删除源文件(父进程来完成)
                string newfilename = NewFileName(filename_);
                pid_t pid = fork();
                if (pid == 0)
                {
                    std::filesystem::current_path(filepath_);
                    execlp("tar", "tar", "-zcf", newfilename.c_str(), filename_.c_str(), NULL);
                    exit(1); // 表示进程替换失败
                }

                // 父进程删除源文件, 并等子进程
                int status;
                pid_t rid = waitpid(pid, &status, 0);
                if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
                {
                    std::filesystem::remove(file.c_str());
                }

                cout << "压缩完成" << '\n';
            }
        }
        cout << "end of chain" << '\n';
    }

protected:
    string filepath_;
    string filename_;
    int max_line_;
};

// 将整个责任链串起来
class ChainOfResponsibility
{
public:
    ChainOfResponsibility(const string &filepath = default_filepath, const string &filename = default_filename, const int max_line = default_max_line)
    {
        // 先创建所有对象
        package_ = std::make_shared<HandlerTextPackage>();
        savefile_ = std::make_shared<HandlerTextSaveFile>(filepath, filename);
        targz_ = std::make_shared<HandlerTextTarGz>(filepath, filename, max_line);

        package_->SetNextHandler(savefile_);
        savefile_->SetNextHandler(targz_);
    }

    // 运行
    void Run(const string &message)
    {
        package_->Excute(message);
    }

    ~ChainOfResponsibility()
    {
    }

protected:
    std::shared_ptr<HandlerTextPackage> package_;
    std::shared_ptr<HandlerTextSaveFile> savefile_;
    std::shared_ptr<HandlerTextTarGz> targz_;
};

#endif // CHAIN_OF_RESPONSIBILITY_HPP